#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cassert>
#include <cstdlib>
#include <pthread.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
//包含unix头文件 fork
#include "log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Thread.hpp"

namespace server
{
    enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR, LISTEN_ERR};

    static const uint16_t gport = 8080;
    static const int gbacklog = 5;

    class TcpServer;//声明一下类
    struct ThreadData
    {
        TcpServer *_this;
        int _sockfd;
    };
    class TcpServer
    {
    public:
        TcpServer(const uint16_t &port = gport)
            :_listenSockfd(-1), _port(port)
        {

        }
        void initServer()
        {
            // 1.创建TCP套接字
            _listenSockfd = socket(AF_INET, SOCK_STREAM, 0);
            if(_listenSockfd == -1)
            {
                logMessage(FATAL, "create socket error!");
                exit(SOCKET_ERR);
            }
            logMessage(NORMAL, "create socket success: listenSockfd(%d)", _listenSockfd);
            int opt = 1;
            setsockopt(_listenSockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
            // 2.bind绑定自己的网络信息, ip+port
            struct sockaddr_in local; // 服务器本地的网络信息
            memset(&local, 0, sizeof(local));
            local.sin_family = PF_INET; // IP protocol family
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY; // 任意ip
            // 为什么recvfrom sendto数据时, 没有hton ntoh呢？因为系统调用已经隐式的做了网络字节序和主机的转换工作.而我们自己写的结构体必须得转换之后才能bind

            if(bind(_listenSockfd, (struct sockaddr *)&local, sizeof(local)) == -1)
            {
                logMessage(FATAL, "bind socket error!");
                exit(BIND_ERR);
            }
            logMessage(NORMAL, "bind socket success");

            // 3.设置服务器的socket为监听状态 --> 监听什么时候客户端发来连接请求
            if(listen(_listenSockfd, gbacklog) == -1) // 第二个参数gbacklog后面再讲
            {
                logMessage(FATAL, "listen socket error!");
                exit(LISTEN_ERR);
            }
            logMessage(NORMAL, "listen socket success");

        }
        void start()
        {               
            ThreadPool<Task>::getInstance()->run();//线程已经跑起来了,RAII风格的线程池
            logMessage(NORMAL, "ThreadPool init success");
            // signal(SIGCHLD, SIG_IGN);//子进程退出的时候, 让linux操作系统帮我们回收子进程资源

            for(;;)//服务器启动后要进行死循环
            {
                // 4.server和client要建立连接, server获取来自client的连接请求
                struct sockaddr_in peer; //远端的客户端信息
                socklen_t len = sizeof(peer);
                int sockfd = accept(_listenSockfd, (struct sockaddr *)&peer, &len);//sockfd是未来和client进行通信的套接字
                if(sockfd == -1)
                {
                    logMessage(ERROR, "accept error, next");
                    //这是一种错误,但这并不影响server和其他的client继续连接然后通信啊,当前这个客户端无法连接成功,不代表其他client也连接不成功啊!
                    continue;
                }
                logMessage(NORMAL, "accept a new link success, get new sockfd: %d", sockfd);
                
                ThreadPool<Task>::getInstance()->push(Task(sockfd, serviceIO));//构建任务, 线程执行任务时会回调serviceIO
            }
        }
        ~TcpServer(){}
    private:
        int _listenSockfd; //核心作用不是用来通信的, 而是用来监听客户端是否有连接请求的
        uint16_t _port;
    };

} // namespace server

// while :; do ps -aL | head -1 && ps -aL | grep tcpServer; sleep 1; echo "#######################"; done ---查看线程的监控脚本































